/**
 * @brief 对socket套接字的操作进行封装
 * @details
 * 1.套接字的基础封装,以及封装了直接创建客户端和服务端,设置套接字非阻塞属性
 */
#pragma once
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include "logger.hpp"
namespace hdp
{
    class Socket
    {
    private:
        int _sockfd;

    public:
        Socket() : _sockfd(-1) {}
        Socket(int fd) : _sockfd(fd) {}
        ~Socket() { Close(); }
        int Fd() { return _sockfd; }

    public:
        // 创建套接字
        bool Create()
        {
            // int socket(int domain,int type,int protocol)
            _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if (_sockfd < 0)
            {
                LOG_ERROR("socket create failed");
                return false;
            }
            return true;
        }

        // 绑定地址信息
        bool Bind(const std::string &ip, const uint16_t &port)
        {
            struct sockaddr_in local;
            memset(&local, 0, sizeof local);

            local.sin_family = AF_INET;
            local.sin_port = htons(port);
            local.sin_addr.s_addr = inet_addr(ip.c_str());
            socklen_t len = sizeof(local);

            // int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
            int ret = bind(_sockfd, (struct sockaddr *)&local, len);
            if (ret < 0)
            {
                LOG_ERROR("socket bind failed");
                return false;
            }
            return true;
        }

        // 开始监听
        bool Listen(int backlog = Config::GetInstance().max_listen_size)
        {
            // int listen(int sockfd, int backlog);
            int ret = listen(_sockfd, backlog);
            if (ret < 0)
            {
                LOG_ERROR("socket listen failed");
                return false;
            }
            return true;
        }

        // 向服务器发起连接
        bool Connect(const std::string &ip, const uint16_t &port)
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof server);

            server.sin_family = AF_INET;
            server.sin_port = htons(port);
            server.sin_addr.s_addr = inet_addr(ip.c_str());
            socklen_t len = sizeof(server);

            // int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
            int ret = connect(_sockfd, (struct sockaddr *)&server, len);
            if (ret < 0)
            {
                LOG_ERROR("socket connect failed");
                return false;
            }
            return true;
        }

        // 获取新连接
        int Accept(std::string &clientIp)
        {
            // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
            struct sockaddr_in addr;
            memset(&addr, 0, sizeof(addr));
            socklen_t len = sizeof(addr);
            int newfd = accept(_sockfd, (struct sockaddr *)&addr, &len);
            if (newfd < 0)
            {
                LOG_ERROR("connect newfd failed");
                return -1;
            }
            clientIp = inet_ntoa(addr.sin_addr);
            return newfd;
        }

        // 接收数据
        ssize_t Recv(void *buf, size_t len, int flags = 0)
        {
            // ssize_t recv(int sockfd, void *buf, size_t len, int flags);
            ssize_t ret = recv(_sockfd, buf, len, flags);
            if (ret <= 0)
            {
                // EAGAIN表示当前socket的接收缓冲区中没有数据了,在非阻塞的情况下才会有这个错误
                // EINTR 表示当前socket的阻塞等待,被信号打断了
                if (errno == EAGAIN || errno == EINTR)
                {
                    return 0;
                }
                LOG_ERROR("recv message failed");
                return -1;
            }
            // 返回实际接收到的长度
            return ret;
        }

        // 非阻塞接收数据
        ssize_t NonBlockRecv(void *buf, size_t len)
        {
            // MSG_DONTWAIT表示当接收为非阻塞
            return Recv(buf, len, MSG_DONTWAIT);
        }

        // 发送数据
        ssize_t Send(const void *buf, size_t len, int flags = 0)
        {
            // ssize_t send(int sockfd, const void *buf, size_t len, int flags);
            ssize_t ret = send(_sockfd, buf, len, flags);
            if (ret < 0)
            {
                if (errno == EINTR || errno == EAGAIN)
                {
                    return 0;
                }
                LOG_ERROR("send message failed");
                return -1;
            }
            // 返回实际发送的长度
            return ret;
        }

        // 非阻塞发送数据
        ssize_t NonBlockSend(const void *buf, size_t len)
        {
            if (len == 0)
                return 0;
            // MSG_DONTWAIT表示当前发送为非阻塞
            return Send(buf, len, MSG_DONTWAIT);
        }

        // 关闭套接字
        void Close()
        {
            if (_sockfd != -1)
            {
                close(_sockfd);
                _sockfd = -1;
            }
        }

        // 创建一个服务端连接
        bool CreateServer(const uint16_t &port, const std::string &ip = Config::GetInstance().server_ip, bool block_flag = Config::GetInstance().block_flag)
        {
            // 1.创建套接字,2.设置非阻塞3.绑定地址,4.开始监听,5.启动地址重用
            if (Create() == false)
                return false;
            ReuseAddress();
            if (block_flag)
                NonBlock(_sockfd);
            if (Bind(ip, port) == false)
                return false;
            if (Listen() == false)
                return false;
            LOG_INFO("create server success");
            return true;
        }

        // 创建一个客户端连接
        bool CreateClient(const uint16_t &port, const std::string &ip)
        {
            // 1.创建套接字,2,连接服务器
            if (Create() == false)
                return false;
            if (Connect(ip, port) == false)
                return false;
            return true;
        }

        // 设置套接字选项 --开启地址端口重用
        void ReuseAddress()
        {
            // int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
            int opt = 1;
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof opt);
            opt = 1;
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (void *)&opt, sizeof opt);
            // int val = 1;
            // setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, (void*)&val, sizeof(val));
        }

        // 设置套接字属性--设置为非阻塞
        static void NonBlock(int fd)
        {
            // int fcntl(int fd, int cmd, ... /* arg */ );
            int flag = fcntl(fd, F_GETFL, 0);
            fcntl(fd, F_SETFL, flag | O_NONBLOCK);
        }
    };
}